//------------------------------------------------------------------------------
///	\file ringbuffer.c
///
///	\brief
///	Ring Buffer implementation file
///
///	This ring buffer began life as a UART I/O mechanism.
///	It has advanced status information like high water-mark and items processed.
///
//	$Id: ringbuff.c 299 2014-09-27 07:34:40Z ac.verbeck@gmail.com $
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//	The MIT License (MIT)
//
//	Copyright (c) 2014 A.C. Verbeck
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.

//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//------------------------------------------------------------------------------
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>

#include "arch.h"
#include "driver.h"
#include "ringbuff.h"

//------------------------------------------------------------------------------
//	RingBuff_HW_Init		- Hardware initialization (RAM init in this case)
//  RingBuff_OS_Init		- OS object initialization
//  RingBuff_GetStatus		- Get current status
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
///	\brief
///		Hardware initialization
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \param	[in]	dp		pointer to RAM used for ring buffer
/// \param	[in]	ct		the number of items in the ring buffer
//------------------------------------------------------------------------------
void RingBuff_HW_Init(RING_BUFFER* rb, uint8_t *dp, uint16_t ct)
{
	memset(dp, 0, ct);															//  Initialize the data buffer
	memset(rb, 0, sizeof(RING_BUFFER));											//  Initialize the ring buffer structure

	rb->data_ptr	= dp;														//  Pointer to the data buffer
	rb->read_ptr	= dp;														//  Read (remove) pointer
	rb->write_ptr	= dp;														//  Write (add) pointer
	rb->buff_sz		= ct;														//  Data buffer size

	rb->init = INIT_RUN_HW;														//  Indicate initialization status
	rb->status = RB_RESET;														//  Indicate status
}
//------------------------------------------------------------------------------
///	\brief
///		OS object initialization
///		Since there are no OS objects required, this is a
///		module reset.
///
/// \param	[in]	rb		pointer to ring buffer structure
//------------------------------------------------------------------------------
void RingBuff_OS_Init(RING_BUFFER* rb)
{
	rb->curr_ct		= 0;														//  Current count
	rb->water_mark	= 0;														//  High water mark
	rb->total_ct	= 0;														//  Total number of items
	rb->rb_full_ct	= 0;														//  Full count
	rb->rb_empty_ct = 0;														//  Empty count

	rb->init |= INIT_RUN_OS;													//  Indicate initialization status
	rb->status = RB_RESET;														//  Indicate status
}
//------------------------------------------------------------------------------
///	\brief
///		Get driver status
///
/// \return driver status
//------------------------------------------------------------------------------
RB_STATUS RingBuff_GetStatus(RING_BUFFER* rb)
{
	uint32_t t = rb->status;
	rb->status = RB_RESET;
	return (t);
}

//------------------------------------------------------------------------------
//  Ring buffer functions:
//		RingBuff_Add:		add bytes to the ring buffer
//		RingBuff_Remove:	remove bytes from the ring buffer
//		RingBuff_Clear:		clear the ring buffer
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
///	\brief
///		add bytes to the ring buffer
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \param	[in]	c		item to be added (FIXME: currently a character)
//------------------------------------------------------------------------------
void RingBuff_Add(RING_BUFFER* rb, uint8_t c)
{
	assert(rb->water_mark<=rb->buff_sz);
	assert(rb->curr_ct<=rb->buff_sz);

	disable_ints();																//  Disable mask-able interrupts
	if (rb->curr_ct < rb->buff_sz) {											//  Buffer is not full
		*rb->write_ptr++ = c;													//  Add an item
		if (rb->write_ptr >= (rb->data_ptr+rb->buff_sz)) {						//  Test for wrap
			rb->write_ptr	= rb->data_ptr;										//  Wrap buffer
		}
		++rb->curr_ct;															//  Boost buffer count
		++rb->total_ct;															//  Boost total count
		if (rb->curr_ct	> rb->water_mark) {										//  Test for high water mark
			rb->water_mark = rb->curr_ct;										//  Set new high water mark
		}
		rb->status = RB_OK;														//  Return status
	} else {																	//  Buffer is full
		rb->status = RB_FULL;													//  Return full status
		++rb->rb_full_ct;														//  Boost RB full count
	}
	enable_ints();																//  Enable mask-able interrupts
}
//------------------------------------------------------------------------------
///	\brief
///		remove a byte from ring buffer
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \param	[in]	c		pointer to item to be removed (FIXME: currently a character)
//------------------------------------------------------------------------------
void RingBuff_Remove(RING_BUFFER* rb, uint8_t *c)
{
	assert(rb->water_mark<=rb->buff_sz);
	assert(rb->curr_ct<=rb->buff_sz);

	disable_ints();																//  Disable mask-able interrupts
	if (rb->curr_ct >	0) {													//  If not empty...
		*c = *rb->read_ptr++;													//  Get an item
		if (rb->read_ptr >=	(rb->data_ptr+rb->buff_sz)) {						//  Test for wrap
			rb->read_ptr = rb->data_ptr;										//  Wrap buffer
		}
		--rb->curr_ct;															//  Lower count
		rb->status = RB_OK;														//  If empty return
	} else {																	//  If empty...
		*c = 0;																	//	Clear return value
		rb->status = RB_EMPTY;													//  If empty return
		++rb->rb_empty_ct;														//  Boost RB empty count
	}
	enable_ints();																//  Enable mask-able interrupts
}
//------------------------------------------------------------------------------
///	\brief
///		clear ring buffer
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \return status of the clear (RB_EMPTY)
//------------------------------------------------------------------------------
void RingBuff_Clear(RING_BUFFER* rb)
{
	disable_ints();																//  Disable mask-able interrupts
	rb->read_ptr	= rb->data_ptr;												//  Read (remove) pointer
	rb->write_ptr	= rb->data_ptr;												//  Write (add) pointer
	rb->curr_ct		= 0;														//  Current count
	enable_ints();																//  Enable mask-able interrupts

	rb->status = RB_EMPTY;														//  return status
}

//------------------------------------------------------------------------------
//  Ring buffer status functions:
//		RingBuff_GetStat:	get ring buffer status
//		RingBuff_ClrStat:	clear the ring buffer status
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
///	\brief
///		get ring buffer status
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \param	[in]	rbs		pointer to ring buffer status structure
//------------------------------------------------------------------------------
void RingBuff_GetStat(RING_BUFFER* rb, RING_BUFFER_STAT* rbs)
{
	disable_ints();																//  Disable mask-able interrupts
	rbs->buff_sz	= rb->buff_sz;												//  Data buffer size
	rbs->curr_ct	= rb->curr_ct;												//  Current count
	rbs->water_mark = rb->water_mark;											//  High water mark
	rbs->total_ct	= rb->total_ct;												//  Total number of items
	rbs->rb_full_ct = rb->rb_full_ct;											//  Full count
	rbs->rb_empty_ct= rb->rb_empty_ct;											//  Empty count
	enable_ints();																//  Enable mask-able interrupts
}
//------------------------------------------------------------------------------
///	\brief
///		clear status
///
/// \param	[in]	rb		pointer to ring buffer structure
//------------------------------------------------------------------------------
void RingBuff_ClrStat(RING_BUFFER* rb)
{
	disable_ints();																//  Disable mask-able interrupts
	rb->water_mark	= 0;														//  Clear high water mark
	rb->total_ct	= 0;														//  Clear total count
	rb->rb_full_ct	= 0;														//  Full count
	rb->rb_empty_ct	= 0;														//  Empty count
	enable_ints();																//  Enable mask-able interrupts
}

//------------------------------------------------------------------------------
//	Ring buffer single-get status functions:
//		RingBuff_GetSize:		get ring buffer size
//		RingBuff_GetWaterMark:	get high water mark
//		RingBuff_GetCount:		get current count
//		RingBuff_GetTotalCount:	get total count
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
///	\brief
///		get single status: ring buffer size
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \return size of the ring buffer
//------------------------------------------------------------------------------
uint16_t RingBuff_GetSize(RING_BUFFER* rb)
{
	return rb->buff_sz;
}
//------------------------------------------------------------------------------
///	\brief
///		get single status: watermark
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \return highest count of items in the ring buffer
//------------------------------------------------------------------------------
uint16_t RingBuff_GetWaterMark(RING_BUFFER* rb)
{
	return rb->water_mark;
}
//------------------------------------------------------------------------------
///	\brief
///		get single status: count
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \return current number of items in the ring buffer
//------------------------------------------------------------------------------
uint16_t	RingBuff_GetCount(RING_BUFFER* rb)
{
	return rb->curr_ct;
}
//------------------------------------------------------------------------------
///	\brief
///		get single status: total characters processed
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \return total number of items processed
//------------------------------------------------------------------------------
uint32_t	RingBuff_GetTotalCount(RING_BUFFER*	rb)
{
	return rb->total_ct;
}
//------------------------------------------------------------------------------
///	\brief
///		get single status: full count
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \return get number of times that the ring buffer has been full
//------------------------------------------------------------------------------
uint32_t	RingBuff_GetFullCount(RING_BUFFER* rb)
{
	return rb->rb_full_ct;
}
//------------------------------------------------------------------------------
///	\brief
///		get single status: empty count
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \return get number of times that the ring buffer has been empty
//------------------------------------------------------------------------------
uint32_t	RingBuff_GetEmptyCount(RING_BUFFER*	rb)
{
	return rb->rb_empty_ct;
}

//------------------------------------------------------------------------------
//  Ring buffer test functions:
//		RingBuff_Full:	test if ring buffer is full
//		RingBuff_Empty:	test if ring buffer is empty
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
///	\brief
///		test for full ring buffer
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \return true if ring buffer is full, false otherwise
//------------------------------------------------------------------------------
bool RingBuff_Full(RING_BUFFER*	rb)
{
	return (rb->curr_ct == rb->buff_sz) ? true : false;
}
//------------------------------------------------------------------------------
///	\brief
///		test for empty ring buffer
///
/// \param	[in]	rb		pointer to ring buffer structure
/// \return true if ring buffer is empty, false otherwise
//------------------------------------------------------------------------------
bool RingBuff_Empty(RING_BUFFER* rb)
{
	return (rb->curr_ct == 0) ? true : false;
}

//
//  End: ringbuff.c
//
